home *** CD-ROM | disk | FTP | other *** search
- Polyphonic Music on the IBM PC
- Steve Muenter
- Rocketdyne Microcomputer Users Group
-
- Sitting at the keyboard of my IBM PC, I realized that other computers could
- produce music with three simultaneous tones for chords and harmony while my
- IBM was limited to only one tone at a time. To rectify this, I wrote an
- assembly language program which produces three tones from the IBM
- PC's internal speaker without any hardware modification.
-
- Normally, a single tone is generated through the speaker using the 8253
- Programmable Interval Timer. The microprocessor loads a divisor into
- the 8253's register for counter 2. The 1.19 MHz system clock is divided
- by this number and the resulting square wave is sent to the speaker.
- Once the divisor is loaded, the microprocessor is free to do
- something else while the tone plays in the background.
-
- Since the hardware is not set up to produce three tones simultaneously,
- it becomes necessary to get the microprocessor to do the dividing of
- the system clock. The program loop which performs the divisions for the
- three tones must be very quick in order to produce tones with high
- audio frequencies. I found when using memory to store intermediate
- division results, the relatively slow memory accesses took too many clock
- cycles giving three low growls at best. Using the 8088 internal
- registers speeded up the loop considerably since the register
- accesses are about ten times faster than memory accesses.
-
- The assembly language routine presented here was written to be
- called from BASIC. The code is divided into three sections. The
- first section (from "tri" to "sort") sets up the pointers to the data
- passed to the routine. Also, the 8088 internal registers are zeroed.
-
- The second section (from "sort" to "loop") sorts the 16-bit integer
- numbers passed to the routine. The three most significant bits of the
- integer identify the remaining 13 bits as either voice 1, 2, or 3
- period data, note duration data, tempo data or end-of-song flag. The
- bit pattern 000 identifies that the song has ended and causes the routine
- to return to BASIC. The pattern 001 sets the duration of time that the
- current notes will play before the next notes start. The pattern 010
- sets the tempo of the playback. The pattern 100 identifies that the 13
- remaining bits represent period data for voice 1. The pattern 101
- identifies voice 2 period data, and the pattern 110 identifies voice 3
- data.
-
- The last section (from "loop" to "end") performs the division of the
- system clock and switching of the speaker for each of the three voices.
- This is the section of the program that actually makes the tones. For
- speed, the routine for each voice is written entirely rather than written
- as a single subroutine called three times. This was done because jumps
- and calls require at least fifteen clock cycles.
-
- Although the assembly language routine parses the 16-bit binary data
- into three bits identifying data type and thirteen bits representing a
- numerical value, BASIC treats this data as an array of 2's complement
- 16-bit single precision integers. This means that the decimal
- equivalent of the number is given by the following equation.
-
-
- N = -2^15*a(15) + 2^14*a(14) +
- 2^13*a(13) + 2^12*a(12) +
- 2^11*a(11) + 2^10*a(10) +
- 2^9*a(9) + 2^8*a(8) + 2^7*a(7) +
- 2^6*a(6) + 2^5*a(5) + 2^4*a(4) +
- 2^3*a(3) + 2^2*a(2) + 2^1*a(1) +
- 2^0*a(0)
-
-
- where a(n) is a 1 or 0 in the nth bit of the 16 bit number. Notice that
- the most significant bit (the 16th bit) represents a negative number
- (-32768) whereas all the other bits represent positive numbers. The
- numerical range of the 2's complement integers is -32768 to 32767.
-
- It can be seen that the pattern of the three most significant bits can
- be represented as a decimal number. The pattern for identifying duration
- data (001) corresponds to a decimal value of 8192. Tempo data (010)
- equals 16384, voice 1 data (100) equals -32768, voice 2 data (101)
- equals -24576, and voice three data (110) equals -16384. The 13
- remaining bits can represent decimal numbers ranging from 0 to 16383.
-
- The accompanying BASIC program contains data statements which load
- an integer array with the data for playing the first part of the Rondo
- Alla Turca by Wolfgang A. Mozart. The first entry in the integer data
- array sets the tempo of the playback. A typical tempo value of 512 plus the
- tempo identifier 16384 gives the integer data value 16896.
-
- A note's duration is specified by its length relative to a 32nd note. For
- example, a quarter note equals eight 32nd notes, so a decimal value of 8
- is added to the duration identifier, 8192. The resulting number is 8192 +
- 8 = 8200. Likewise, a whole note is represented by the decimal integer
- 8192 + 32 = 8224.
-
- The voice data is determined by adding the number representing the
- frequency of the desired note to the number identifying the desired voice
- of the three possible. The following table gives the numbers representing
- the notes of the scale. For example, middle C on voice 1 is represented by
- adding the voice 1 identifier with the value for middle C. The
- resulting number is -32768 + 1024 = -31744. To turn voice 1 off, the
- data would be -32768 + 0 = -32768.
-
-
- NOTE DATA TABLE
-
- LOW MIDDLE HIGH
- --- ------ ----
- C = 512 C = 1024 C = 2048
- C# = 542 C# = 1085 C# = 2170
- D = 575 D = 1149 D = 2299
- D# = 609 D# = 1218 D# = 2435
- E = 645 E = 1290 E = 2580
- F = 683 F = 1367 F = 2734
- F# = 724 F# = 1448 F# = 2896
- G = 767 G = 1534 G = 3069
- G# = 813 G# = 1625 G# = 3251
- A = 861 A = 1722 A = 3444
- A# = 912 A# = 1825 A# = 3649
- B = 967 B = 1933 B = 3866
- No tone = 0
-
- [Editor's note : The programs discussed above can be found on this diskette
- as separate files, TRI.BIN, TRI.ASM and TRTEST.BAS. The programs do not
- operate properly on the PCjr or the PC AT due to timing considerations.]
-